package top.cardone.data.support.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.collections.MapUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import top.cardone.context.ApplicationContextHolder;
import top.cardone.core.util.func.Func0;
import top.cardone.core.util.func.Func1;
import top.cardone.data.support.PageSupport;
import top.cardone.data.support.PageableSupport;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * Created by yao hai tao on 2015/8/9.
 */
public class PageSupportImpl implements PageSupport {
    /**
     * 集合参数名
     */
    @lombok.Setter
    private String contentParam = "datas";
    /**
     * 是否有下一页参数名
     */
    @lombok.Setter
    private String hasNextParam = "hasNext";
    /**
     * 是否有上一页参数名
     */
    @lombok.Setter
    private String hasPreviousParam = "hasPrevious";
    /**
     * 下一页页码参数名
     */
    @lombok.Setter
    private String nextParam = "next";
    /**
     * 当前页页码参数名
     */
    @lombok.Setter
    private String pageParam = "page";
    /**
     * 当前页范围值参数名
     */
    @lombok.Setter
    private String pagesParam = "pages";
    /**
     * 当前页范围参数名
     */
    @lombok.Setter
    private String pagesRangeParam = "pagesRange";
    /**
     * 上一页页码参数名
     */
    @lombok.Setter
    private String previousParam = "previous";
    /**
     * 总记录条数参数名
     */
    @lombok.Setter
    private String totalElementsParam = "totalElements";
    /**
     * 总页数参数名
     */
    @lombok.Setter
    private String totalPagesParam = "totalPages";

    @Override
    public <T> Map<String, Object> newMap(List<T> content, Map<String, ?> paramMap, long total) {
        Map<String, Object> pageMap = Maps.newHashMap();

        pageMap.put(this.totalElementsParam, total);

        if (total < 1) {
            return pageMap;
        }

        Pageable pageable = ApplicationContextHolder.getBean(PageableSupport.class).newPageable(paramMap);

        Page<T> page = newPage(content, pageable, total);

        pageMap.put(this.totalPagesParam, page.getTotalPages());
        pageMap.put(this.contentParam, page.getContent());
        pageMap.put(this.hasPreviousParam, pageable.hasPrevious());

        if (pageable.hasPrevious()) {
            pageMap.put(this.previousParam, Math.min(pageable.previousOrFirst().getPageNumber() + 1, page.getTotalPages()));
        }

        boolean hasNext = pageable.next().getPageNumber() < page.getTotalPages();

        pageMap.put(this.hasNextParam, hasNext);

        if (hasNext) {
            pageMap.put(this.nextParam, Math.min(pageable.next().getPageNumber() + 1, page.getTotalPages()));
        }

        pageMap.put(this.pageParam, Math.min(page.getNumber() + 1, page.getTotalPages()));

        int pagesRange = MapUtils.getIntValue(paramMap, this.pagesRangeParam, 6);

        if (pagesRange < 1) {
            return pageMap;
        }

        final int pagesRangeHalf = new java.math.BigDecimal(pagesRange).divide(new BigDecimal(2), BigDecimal.ROUND_HALF_UP).toBigInteger().intValue();

        int pagesRangeStart = Math.max(page.getNumber() + 1 - pagesRangeHalf, 1);

        final int pagesRangeEnd = Math.min((pagesRangeStart + pagesRange) - 1, page.getTotalPages());

        if (((pagesRangeEnd - pagesRangeStart) + 1) < pagesRange) {
            pagesRangeStart = Math.max((pagesRangeEnd - pagesRange) + 1, 1);
        }

        int pagesRangeLength = pagesRangeEnd - pagesRangeStart;

        int[] pages = new int[pagesRangeLength + 1];

        for (int i = 0; i <= pagesRangeLength; i++) {
            pages[i] = pagesRangeStart++;
        }

        pageMap.put(this.pagesParam, pages);

        return pageMap;
    }

    @Override
    public <T> Page<T> newPage(List<T> content, Map<String, ?> paramMap, long total) {
        Pageable pageable = ApplicationContextHolder.getBean(PageableSupport.class).newPageable(paramMap);

        return newPage(content, pageable, total);
    }

    @Override
    public <T> Page<T> newPage(List<T> content, Pageable pageable, long total) {
        return new PageImpl<>(content, pageable, total);
    }

    @Override
    public <T> Page<T> newPage(Func1<List<T>, Pageable> contentFunc, Map<String, ?> paramMap, Func0<Long> totalFunc) {
        long total = totalFunc.func();

        if (total < 1) {
            return new PageImpl<>(Lists.newArrayList());
        }

        Pageable pageable = ApplicationContextHolder.getBean(PageableSupport.class).newPageable(paramMap);

        if (total < pageable.getOffset()) {
            return new PageImpl<>(Lists.newArrayList());
        }

        List<T> content = contentFunc.func(pageable);

        return new PageImpl<>(content, pageable, total);
    }

    @Override
    public <T> Page<T> newPage(Func0<List<T>> contentFunc, Pageable pageable, Func0<Long> totalFunc) {
        long total = totalFunc.func();

        if ((total < 1) || (total < pageable.getOffset())) {
            return new PageImpl<>(Lists.newArrayList());
        }

        List<T> content = contentFunc.func();

        return new PageImpl<>(content, pageable, total);
    }
}